/**
 * 控制聊天侧边栏的显示
 */
import { getLogger } from '@gnlab/utils'
import { OrderedSet } from 'immutable'
import { type FunctionComponent, lazy } from 'react'
import { type Subscription } from 'rxjs'
import { useStreams } from '@/lib/utils'
import useUIMemory from '@/logics/memory'
import services from '@/services'
import { type SystemSidebar, type CustomSidebar } from '@/services/features/chat-sidebars'
import CustomPage from './custom-page'

const logger = getLogger('ui/sidebars')

type Component = (() => JSX.Element) | FunctionComponent
type RenderSidebar = Pick<
  SystemSidebar,
  'key' | 'name' | 'scope' | 'supportObserving' | 'reset' | 'extraVisible' | 'disabled'
> & { Component: Component }

// 各侧边栏内容延迟打包 / 加载
const ExternalInfo = lazy(async () => import('./external-info'))
const WordsLibrary = lazy(async () => import('./words-library'))
const NewRank = lazy(async () => import('./new-rank'))
const GroupInfo = lazy(async () => import('./group-info'))
const GroupMembers = lazy(async () => import('./group-members'))
const GroupTools = lazy(async () => import('./group-tools'))
const components: Record<string, Component> = {
  'external-info': ExternalInfo,
  'words-library': WordsLibrary,
  'new-rank': NewRank,
  'group-info': GroupInfo,
  'group-members': GroupMembers,
  'group-tools': GroupTools,
}

function formatSystemSidebar(sidebar: SystemSidebar): RenderSidebar | null {
  const component = components[sidebar.key]
  if (component) return { ...sidebar, Component: component }
  else {
    logger.warn('系统侧边栏找不到对应组件', sidebar)
    return null
  }
}
function formatCustomSidebar(sidebar: CustomSidebar): RenderSidebar {
  return {
    ...sidebar,
    supportObserving: true,
    reset: true,
    Component: () => <CustomPage url={sidebar.url} />,
  }
}

// show: 是否开启侧边栏（仍要根据是否有可用的侧边栏来决定是否显示）
// sidebar: 要显示哪个侧边栏（如果此侧边栏不可用，会改为显示第一个）
const defaultSetting = { show: false, sidebar: null }

export function useChatSidebars() {
  const [sidebars, contact, observingReadonly] = useStreams(
    services.features.chatSidebars.list,
    services.contacts.current.contact,
    services.auth.observing.readonly$,
  )
  const contactType = contact?.type
  const renderSiderbars = useMemo(() => {
    return [...sidebars]
      .sort((a, b) => (a.system ? 1 : 2) - (b.system ? 1 : 2)) // 系统侧边栏在这里优先显示
      .map(v => (v.system ? formatSystemSidebar(v) : formatCustomSidebar(v))) // 转成前端渲染所需的格式
      .filter((v): v is RenderSidebar => v !== null) // 过滤转换失败的
      .filter(sidebar => {
        // 根据使用范围初步过滤
        if (sidebar.disabled) return false
        if (contactType === undefined || !sidebar.scope.includes(contactType)) return false
        if (observingReadonly && !sidebar.supportObserving) return false
        return true
      })
  }, [sidebars, contactType, observingReadonly])

  // 根据 extraVisible 进一步过滤
  const [extraFiltered, setExtraFiltered] = useState(OrderedSet<string>())
  useEffect(() => {
    const subscriptions: Subscription[] = []
    renderSiderbars.forEach(sidebar => {
      if (!sidebar.extraVisible) return
      const subscription = sidebar.extraVisible.subscribe(visible => {
        setExtraFiltered(extraFiltered => {
          if (visible && extraFiltered.has(sidebar.key)) return extraFiltered.delete(sidebar.key)
          if (!visible && !extraFiltered.has(sidebar.key)) return extraFiltered.add(sidebar.key)
          return extraFiltered
        })
      })
      subscriptions.push(subscription)
    })
    return () => subscriptions.forEach(sub => sub.unsubscribe())
  }, [renderSiderbars])
  const usableSidebars = useMemo(
    () => renderSiderbars.filter(v => !extraFiltered.has(v.key)),
    [renderSiderbars, extraFiltered],
  )

  const [setting, setSetting] = useUIMemory<{ show: boolean; sidebar: string | null }>(
    'chat-sidebar-tab-v2',
    defaultSetting,
  )
  const showable = !!usableSidebars.length
  const showing = showable && setting.show
  const switchShow = useCallback(() => {
    if (showable) setSetting({ ...setting, show: !setting.show })
  }, [setting, setSetting, showable])

  const showingSidebar = useMemo(() => {
    if (!showing) return null
    return setting.sidebar !== null
      ? usableSidebars.find(t => t.key === setting.sidebar) ?? usableSidebars[0]!
      : usableSidebars[0]!
  }, [showing, setting, usableSidebars])

  const setSidebar = useCallback(
    (sidebar: string) => setSetting({ ...setting, sidebar }),
    [setting, setSetting],
  )

  return {
    switchShow: showable ? switchShow : null, // 切换侧边栏开启状态，为 null 代表当前不能切换状态
    showingSidebar, // 当前显示的侧边栏，为 null 代表不显示侧边栏
    usableSidebars, // 当前可供显示的侧边栏
    setSidebar, // 切换要显示的侧边栏（不影响侧边栏开启 / 关闭）
  }
}
